#include "eulerOperators.h"
#include "object.h"
#include "halfEdge.h"
#include "vector.h"

#define MINUS 0
#define PLUS 1

EulerOperators::EulerOperators()
{
    idFace = 0;
    idLoop = 0;
    idEdge = 0;
    idHalfEdge = 0;
    idVertex = 0;
}

HalfEdge *EulerOperators::addHE(Edge *_e, Vertex *_v, HalfEdge *_he, bool _sign)
{
    HalfEdge *newHE;

    if (!_he->getEdge())
    {
        //caso onde esteja criando a primeira aresta (inserindo o segundo ponto)
        newHE = _he;
    }
    else
    {
        //caso esteja criando uma nova aresta para ligar duas faces
        newHE = new HalfEdge(idHalfEdge++);

        //setando a lista encadeada de half-edge
        //achando a anterior a _he
        HalfEdge *prevHE = hePrev(_he);
        //setando o next dela
        prevHE->setNext(newHE);

        //setando o next da nova half-edge
        newHE->setNext(_he);
    }

    //setando os valores de half-edge
    newHE->setVertex(_v);
    newHE->setLoop(_he->getLoop());
    newHE->setEdge(_e);

    if (_sign == PLUS)
        _e->setHE1(newHE);
    else
        _e->setHE2(newHE);

    return newHE;
}


HalfEdge *EulerOperators::delHE(HalfEdge *_he)
{
    if(!_he->getEdge())
    {
//        std::cout <<"[delHE] Está entrando no que não existe aresta" <<std::endl;
        //se a aresta da half-edge não existir (caso do primeiro ponto)
        //deleta-se a halfedge e pronto
        delete _he;
        return NULL;
    }
    else
    {
        if(_he->getNext() == _he)
        {

//            std::cout <<"[delHE] Está entrando no que o next é ele mesmo" <<std::endl;
            //??
            _he->setEdge(NULL);
            return _he;
        }
        else
        {
//            std::cout <<"[delHE] Está entrando no normal" <<std::endl;
            HalfEdge *prev = hePrev(_he);
            prev->setNext(_he->getNext());

            delete _he;
            return prev;
        }
    }

//    std::cout <<"[delHE] Está retornando o ultimo NULL" <<std::endl;
    return NULL;
}

HalfEdge *EulerOperators::findHE(Face *_f, Vertex *_v1, Vertex *_v2)
{
    //acha a half-edge que vai de _v1 para _v2 na face _f

    //preparando para andar no loop
    Loop *l = _f->getOutLoop();
    HalfEdge *he = l->getHERing();

    do{
        //andando no loop

        //testa se essa half edge é a que se quer
        //ou quando conecta os dois vertices, ou quando sai do primeiro vertice (_v2 == NULL)
        if ((he->getVertex() == _v1) && ( (he->getNext()->getVertex() == _v2) || (!_v2) ) )
            return he;

        he = he->getNext();
    }while (he != l->getHERing());

    for(unsigned int i = 0; i < _f->getInLoopsSize(); i++)
    {
        l = _f->getInLoop(i);
        HalfEdge *he = l->getHERing();

        do{
            //andando no loop

            //testa se essa half edge é a que se quer
            //ou quando conecta os dois vertices, ou quando sai do primeiro vertice (_v2 == NULL)
            if ((he->getVertex() == _v1) && ( (he->getNext()->getVertex() == _v2) || (!_v2) ) )
                return he;

            he = he->getNext();
        }while (he != l->getHERing());
    }



    std::cout <<"[findHE] Não foi achado o loop" <<std::endl;
    return NULL;
}

HalfEdge *EulerOperators::hePrev(HalfEdge *_he)
{
    //procurando a aresta anterior a dada de entrada
    HalfEdge *prevHE = _he;
    while(prevHE->getNext() != _he)
        prevHE = prevHE->getNext();

    return prevHE;
}

void EulerOperators::mvfs(Object *_obj, const Vector3D &_p, const Color &_color)
{
    //criando loop
    Loop *newLoop = new Loop(idLoop++);

    //criando face e setando todos os parametros
    Face *newFace = new Face(idFace++, _obj, newLoop);
    _obj->addFace(newFace);

    //criando primeiro vertice e setando alguns parametros
    Vertex *newVertex = new Vertex(idVertex++, _p, _color);
    _obj->addVertex(newVertex);

    //criando priemira half-edge
    HalfEdge *he = new HalfEdge(idHalfEdge++, newVertex, newLoop);

    //acabando de setar loop
    newLoop->setFace(newFace);
    newLoop->setHERing(he);

    //acabando de setar vertice
    newVertex->setHE(he);

    //setando half-edge
    he->setNext(he);
}

void EulerOperators::lmev(Object *_obj, HalfEdge *_he1, HalfEdge *_he2, const Vector3D &_p, const Color &_color)
{
    //criando o novo vertice, setando quase tudo e colocando no objeto
    Vertex *newVertex = new Vertex(idVertex++, _p, _color);
    _obj->addVertex(newVertex);

    //criando nova aresta
    Edge *newEdge = new Edge(idEdge++);
    _obj->addEdge(newEdge);

    //trocando o vertice das half edge
    HalfEdge *he = _he1;
    while ( he != _he2 )
    {
        he->setVertex(newVertex);
        he = he->getTwin()->getNext();
    }

    //adicionando de fato as half-edges
    addHE(newEdge, _he2->getVertex(), _he1, MINUS);
    addHE(newEdge, newVertex, _he2, PLUS);

    //setando a halfedge dos vertices
    newVertex->setHE(hePrev(_he2));
    _he2->getVertex()->setHE(_he2);
}

void EulerOperators::mev(Object *_obj, Face *_f1, Face *_f2, Vertex *_v1, Vertex *_v2, Vertex *_v3, const Vector3D &_p, const Color &_color)
{
    if(!_obj)
    {
        std::cout << "[MEV] Objeto inexistente" << std::endl;
        return;
    }

    if(!_f1)
    {
        std::cout << "[MEV] Face 1 inexistente" << std::endl;
        return;
    }

    if(!_f2)
    {
        std::cout << "[MEV] Face 2 inexistente" << std::endl;
        return;
    }

    HalfEdge *he1, *he2;

    if( !(he1 = findHE(_f1, _v1, _v2)) )
    {
        std::cout << "[MEV] Half-edge 1 inexistente" << std::endl;
        return;
    }

    if( !(he2 = findHE(_f2, _v1, _v3)) )
    {
        std::cout << "[MEV] Half-edge 2 inexistente" << std::endl;
        return;
    }

    lmev(_obj, he1 , he2, _p, _color);
}

void EulerOperators::mev(Object *_obj, Face *_f1, Vertex *_v1, Vertex *_v2, const Vector3D &_p, const Color &_color)
{
    if(!_obj)
    {
        std::cout << "[MEV] Objeto inexistente" << std::endl;
        return;
    }

    if(!_f1)
    {
        std::cout << "[MEV] Face inexistente" << std::endl;
        return;
    }

    HalfEdge *he1;

    if( !(he1 = findHE(_f1, _v1, _v2)) )
    {
        std::cout << "[MEV] Half-edge 1 inexistente" << std::endl;
        return;
    }

    lmev(_obj, he1 , he1, _p, _color);
}

void EulerOperators::lkev(Object *_obj, HalfEdge *_he1, HalfEdge *_he2)
{
    //o vertice e a aresta removidas é de _he1

    //pegando ponteiros a serem usados
    Edge *e = _he1->getEdge();
    Vertex *v1 = _he1->getVertex();
    Vertex *v2 = _he2->getVertex();
    HalfEdge *he1Twin = _he1->getTwin();
    Loop *loop = _he1->getLoop();
    Loop *loopTwin = he1Twin->getLoop();

    //setar do vertices de todas as halfedges do twin até a _he1
    HalfEdge *he = he1Twin->getNext();
    while ( he != _he1 )
    {
        he->setVertex(v2);
        he = he->getTwin()->getNext();
    }

    //deletando as halfedges da aresta a ser deletada
    HalfEdge *prevHE1 = delHE(_he1);
    HalfEdge *prevHE1twin = delHE(he1Twin);

    //setando a halfedge do vertice que fica
    v2->setHE(prevHE1->getNext());

    //setando os loops só ara garantir
    loop->setHERing(prevHE1);
    loopTwin->setHERing(prevHE1twin);

    //deltando de fato vertice e aresta
    e->setHE(NULL, NULL);
    _obj->delEdge(e);
    _obj->delVertex(v1);

}

void EulerOperators::kev(Object *_obj, Face *_f, Vertex *_v1, Vertex *_v2)
{
    if(!_obj)
    {
        std::cout << "[KEV] Objeto inexistente" << std::endl;
        return;
    }

    if(!_f)
    {
        std::cout << "[KEV] Face inexistente" << std::endl;
        return;
    }


    HalfEdge *he1, *he2;

    if( !(he1 = findHE(_f, _v1, _v2)) )
    {
        //é uma halfedge que o vertice de começo é v1 (é o vertice removido)
        std::cout << "[KEV] Half-edge 1 inexistente" << std::endl;
        return;
    }

    if( !(he2 = findHE(_f, _v2, NULL)) )
    {
        //é uma halfedge que o vertice de começo é v2
        std::cout << "[KEV] Half-edge 2 inexistente" << std::endl;
        return;
    }

    lkev(_obj, he1 , he2);
}

void EulerOperators::lmef(Object *_obj, HalfEdge *_he1, HalfEdge *_he2)
{
    //criando o novo loop
    Loop *newLoop = new Loop(idLoop++);

    //criando nova face com todos os sets
    Face *newFace = new Face(idFace++, _obj, newLoop);
    _obj->addFace(newFace);

    //criando a nova aresta
    Edge *newEdge = new Edge(idEdge++);
    _obj->addEdge(newEdge);

    //setando face de loop
    newLoop->setFace(newFace);


    HalfEdge *he;
    //setando o loop de todas as half-edges entre as dadas e entrada
    he = _he1;
    while ( he != _he2 ){
        he->setLoop(newLoop);
        he = he->getNext();
    }

    HalfEdge *newHE1, *newHE2;
    //incluindo criando as novas half-edges para a nova aresta criada
    newHE1 = addHE(newEdge, _he2->getVertex(), _he1, MINUS);
    newHE2 = addHE(newEdge, _he1->getVertex(), _he2, PLUS);

    //porem os nexts das half edges anteriores a half-edge enviada estão trocados
    //como não se tem guradado os prevs, há a necessidade de procurar a half-edge que era anterior as escolhidas para setar seus nexts
    he = _he1;
    while (he->getNext() != newHE2)
    {
        he = he->getNext();
    }
    he->setNext(newHE1);

    he = _he2;
    while (he->getNext() != newHE1)
    {
        he = he->getNext();
    }
    he->setNext(newHE2);

    //setando as half-edges dos loops (antigo e novo)
    newLoop->setHERing(newHE1);
    _he2->getLoop()->setHERing(newHE2);
}

void EulerOperators::mef(Object *_obj, Face *_f, Vertex *_v1, Vertex *_v2)
{
    if(!_obj)
    {
        std::cout << "[MEF] Objeto inexistente" << std::endl;
        return;
    }

    if(!_f)
    {
        std::cout << "[MEF] Face 1 inexistente" << std::endl;
        return;
    }

    HalfEdge *he1, *he2;

    if( !(he1 = findHE(_f, _v1, NULL)) ){
         std::cout << "[MEF] Half-edge do vertice 1 inexistente" << std::endl;
         return;
    }

    he2 = he1;
    while ( he2->getVertex() != _v2 ){
        he2 = he2->getNext();
        if (he2 == he1){
             std::cout << "[MEF] Half-edge do vertice 2 inexistente" << std::endl;
             return;
        }
    }

    lmef(_obj, he1 , he2);
}

void EulerOperators::lkef(Object *_obj, HalfEdge *_he1)
{
    //pegnado todos os ponteiros necessários
    Edge *e = _he1->getEdge();
    HalfEdge *he1Twin = _he1->getTwin();
    HalfEdge *he1Next = _he1->getNext();
    HalfEdge *he1TwinNext = he1Twin->getNext();
    Loop *loop = _he1->getLoop();
    Loop *loopTwin = he1Twin->getLoop();
    Vertex *v = _he1->getVertex();
    Vertex *vTwin = he1Twin->getVertex();

    //setando o loop das halfedges do loop a ser removido
    HalfEdge *he = _he1;
    while ( he != _he1 ){
        he->setLoop(loopTwin);
        he = he->getNext();
    }

    //deletar as halfedges
    HalfEdge *he1Prev = delHE(_he1);
    HalfEdge *he1TwinPrev = delHE(he1Twin);

    //setar os nexts das he retornadas no delete
    he1Prev->setNext(he1TwinNext);
    he1TwinPrev->setNext(he1Next);

    //setar as HE dos vertices
    v->setHE(he1TwinNext);
    vTwin->setHE(he1Next);

    //setar o loop
    loopTwin->setHERing(he1Next);

    //deletando a face e a aresta
    e->setHE(NULL, NULL);
    _obj->delEdge(e);
    _obj->delFace(loop->getFace()); //aqui ja deleta o loop

}

void EulerOperators::kef(Object *_obj, Face *_f, Vertex *_v1, Vertex *_v2)
{
    if(!_obj)
    {
        std::cout << "[KEF] Objeto inexistente" << std::endl;
        return;
    }

    if(!_f)
    {
        std::cout << "[KEF] Face 1 inexistente" << std::endl;
        return;
    }

    HalfEdge *he1;

    if( !(he1 = findHE(_f, _v1, _v2)) ){
        if( !(he1 = findHE(_f, _v2, _v1)) ){
            std::cout << "[KEF] Half-edge do vertice 1 inexistente" << std::endl;
            return;
        }
    }

    lkef(_obj, he1);
}

void EulerOperators::lkemr(Object *_obj, HalfEdge *_he1)
{
    //pegnado o que será usado
    Edge *e = _he1->getEdge();
    HalfEdge *he1Twin = _he1->getTwin();
    Loop *outLoop = _he1->getLoop();
    Face *f = outLoop->getFace();

    //criando o novo loop
    Loop *newLoop = new Loop(idLoop++, f);
    f->addInLoop(newLoop);

    //trocando os nexts para poder usar o del
    HalfEdge *he3 = _he1->getNext();
    _he1->setNext(he1Twin->getNext());
    he1Twin->setNext(he3);

    //atualizando as halfedges dos vertices
    Vertex *v = _he1->getVertex();
    if(v->getHE() == _he1)
    {
        v->setHE(_he1->getNext());
    }
    v = he1Twin->getVertex();
    if(v->getHE() == he1Twin)
    {
        v->setHE(he3);
    }

    //atualizando os loops das half-edges
    HalfEdge *he4 = he1Twin;
    do
    {
        he4->setLoop(newLoop);
        he4 = he4->getNext();
    }while(he4 != he1Twin);

    //atualizando as half-edges dos loops
    he3 = delHE(_he1);
    outLoop->setHERing(he3);
    he4 = delHE(he1Twin);
    newLoop->setHERing(he4);

    //deletando a aresta
    e->setHE(NULL, NULL);
    _obj->delEdge(e);
}

void EulerOperators::kemr(Object *_obj, Face *_f, Vertex *_v1, Vertex *_v2)
{
    HalfEdge *he1;

    //o loop de fora tem que está no vertice de he1
    if( !(he1 = findHE(_f, _v1, _v2)) ){
        std::cout << "[KEMR] Half-edge do vertice 1 inexistente" << std::endl;
        return;
    }

    lkemr(_obj, he1);
}

void EulerOperators::lkfmrh(Object *_obj, Face *_f1, Face *_f2)
{
    //pegar o o loop da segunda face e
    //coloca como um loop interno da primeira usando ringmv
    ringmv(_f2, _f1, _f2->getOutLoop(), false);

    //deletar a segunda face
    _obj->delFace(_f2);
}

void EulerOperators::kfmrh(Object *_obj, Face *_f1, Face *_f2)
{
//
//    HalfEdge *he1;
//
//    //o loop de fora tem que está no vertice de he1
//    if( !(he1 = findHE(_f, _v1, _v2)) ){
//        std::cout << "[KEMR] Half-edge do vertice 1 inexistente" << std::endl;
//        return;
//    }

    lkfmrh(_obj, _f1, _f2);
}

void EulerOperators::lringmv(Loop *_l, Face *_f, bool _inOut)
{
	//se _inOut == true , o loop será o loop de fora da face
	//caso contrario, se torna um loop interno
	if(_inOut)
	{
	    Loop *l = _f->getOutLoop();
	    if(l) delete l;

	    _f->setOutLoop(_l);
	}else
	{
	    _f->addInLoop(_l);
	}
    _l->setFace(_f);
}

void EulerOperators::ringmv(Face *_f1, Face *_f2, Vertex *_v1, Vertex *_v2, bool _inOut)
{
    //coloca o ring que está na face 1 para a face dois

    HalfEdge *he1;

    if( !(he1 = findHE(_f1, _v1, _v2)) ){
        std::cout << "[RingMV] Half-edge do vertice 1 para o vertice 2 é inexistente" << std::endl;
        return;
    }

    Loop *l = he1->getLoop();
    _f1->removeLoop(l);

    lringmv(l, _f2, _inOut);
}

void EulerOperators::ringmv(Face *_f1, Face *_f2, Loop *_l, bool _inOut)
{
    //coloca o ring que está na face 1 para a face 2
    _f1->removeLoop(_l);

    lringmv(_l, _f2, _inOut);
}

void EulerOperators::sweep(Face *_f, const Vector3D &_p)
{
    Object *obj = _f->getObject();

    //fazendo o sweep para cada loop da face
    sweepByLoop(_f->getOutLoop(), _p, obj);
    for(unsigned int i = 0; i < _f->getInLoopsSize(); i++)
        sweepByLoop(_f->getInLoop(i), _p, obj);
}

void EulerOperators::sweepByLoop(Loop *_l, const Vector3D &_p, Object *_obj)
{
    //guardando as halfedges para andar pelo loop
    HalfEdge *first, *scan;
    first = _l->getHERing();
    scan = first->getNext();

    //é feita a primeira aresta e cópia de vertice
    Vertex *v = scan->getVertex();
    lmev(_obj, scan, scan, v->getPosition() + _p, v->getColor());

    //são feitas as arestas e faces novas
    while(scan != first){
        //guarda a next só para não usar 4 vezes a função
        HalfEdge *next = scan->getNext();
        v = next->getVertex();

        //cria a proxima aresta junto com o vertice
        //com isso o loops continuam correto, sendo o proxima da half edge criada o seu irmão
        lmev(_obj, next, next, v->getPosition() + _p, v->getColor());

        //cria a proxima face
        //notar que o next do scan foi atualizado com a função passada, por isso não se usa o que está guardado
        lmef(_obj, hePrev(scan), scan->getNext()->getNext());

        //o proximo a ser o scan é realmente o next
        //scan = scan->getNext()->getTwin()->getNext()
        scan = next;
    }
    //feita a ultima face
    lmef(_obj, hePrev(scan), scan->getNext()->getNext());
}

void EulerOperators::rsweep(Object *_obj, const Vector3D &_axis, int _step, const Vector3D &_center)
{
    //fazendo o rsweep para cada loop da face
    Face *f = _obj->getFace(0);
//    Vector3D center = _obj->calculateCenter();

    rsweepByLoop(f->getOutLoop(), _axis, _step, _center, _obj);
    for(unsigned int i = 0; i < f->getInLoopsSize(); i++)
        rsweepByLoop(f->getInLoop(i), _axis, _step, _center, _obj);
}

void EulerOperators::rsweepByLoop(Loop *_l, const Vector3D &_axis, int _step, const Vector3D &_center, Object *_obj)
{
    if(_step < 2) std::cout <<"Aumente o step!!" <<std::endl;
    HalfEdge *first, *cfirst, *last, *scan = NULL;

    //first e last serão as HE que indicam o começo e o final do arco
    //cfirst é sempre o primeiro da coluna
    //o scan anda pelo objeto

    //pegando first
    first = _l->getHERing();
    while(first->getEdge() != first->getNext()->getEdge()) first = first->getNext();

    //pegando last
    last = first->getNext();
    while(last->getEdge() != last->getNext()->getEdge()) last = last->getNext();

    //pecando cfirst como first
    cfirst = first;

    //criando a matriz de rotação
    Matrix rotateMatrix = addRotation(toRad(360.0 / _step), _axis, _center, Vector3D(0,0,0));

    //loop que irá dá a volta de 360 graus
    for(int i = 0; i < _step - 1; i++)
    {
        //guardando variaveis que serão usadas mais de uma vez
        HalfEdge *next = cfirst->getNext();
        Vertex *nextV = next->getVertex();

        //pegnado a nova posição que será usada para a proxima cópia do arco(o primeiro ponto)
        Vector3D position = nextV->getPosition();
        Vector3D newPosition = (rotateMatrix * position.toHomogeneousCoordinates()).fromHomogeneousCoordinates();

        //criando a primeira aresta com a nova posição
        lmev(_obj, next, next, newPosition, nextV->getColor());

        //pegando a variavel scan sendo pertencente a nova aresta criada
        scan = cfirst->getNext();

        //scan vai passar por varias halfedges até chegar no fim
        while(scan != last->getNext())
        {
            //guarndaod variaveis uqe serão usadamais de uma vez
            HalfEdge *prev = hePrev(scan);
            Vertex *prevV = prev->getVertex();

            //pegnaod a nvoa posição
            position = prevV->getPosition();
            newPosition = (rotateMatrix * position.toHomogeneousCoordinates()).fromHomogeneousCoordinates();

            //criando a nova aresta
            lmev(_obj, prev, prev, newPosition, prevV->getColor());

            //pegando a anterior da anterior de scan que agora pertence a nova aresta criada
            prev = hePrev(hePrev(scan));

            //cria a face e aresta entre as duas ultimas arestas criadas
            lmef(_obj, prev, scan->getNext());

            //atualiza scan
            scan = scan->getNext()->getNext()->getTwin();
        }

        //atualiza ponteiros para fazer a proxima copia
        last = scan;
        cfirst = cfirst->getNext()->getNext()->getTwin();
    }

    //cria a primeira das ultimas faces que lig ao começo ao final
    lmef(_obj, cfirst->getNext(), first->getTwin());

    //cria o resto das ultimas faces
    while(cfirst != scan)
    {
        lmef(_obj, cfirst, cfirst->getNext()->getNext()->getNext());
        cfirst = hePrev(hePrev(cfirst)->getTwin());
    }

}
